#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

// 参考资料
// https://cloud.tencent.com/developer/article/1427939
// https://cloud.tencent.com/developer/article/2270873
// https://cloud.tencent.com/developer/article/2270874
// https://blog.csdn.net/shelldon/article/details/54407534

#define JPEG_SOF0  0xFFC0
#define JPEG_DHT   0xFFC4

#define JPEG_SOI   0xFFD8
#define JPEG_EOF   0xFFD9
#define JPEG_SOS   0xFFDA
#define JPEG_DQT   0xFFDB
#define JPEG_DRI   0xFFDD

#define JPEG_APP1  0xFFE1
#define MERGE_2BYTE(addr, _idx_) (*(addr + _idx_) << 8 | *(addr + _idx_ + 1) << 0)
#define MERGE_4BYTE(addr, _idx_) (*(addr + _idx_) << 24 | *(addr + _idx_ + 1) << 16 | *(addr + _idx_ + 2) << 8 | *(addr + _idx_ + 3) << 0)

#define RELATIVE_ADDR(addr, basic_addr) ((addr > basic_addr)?(addr - basic_addr):(basic_addr - addr))

uint8_t *pJpegAddr = NULL; // JPEG图片缓存的起始地址

/**
 * @brief  jpeg_getComponentSize
 * @note   
 * @param [in]
 * @param [out]
 * @retval 
 */
uint16_t jpeg_getComponentSize(uint16_t type)
{
	// Value        Format            Bytes/component 
	//   1       unsigned byte              1
	//   2       ascii strings              1
	//   3       unsigned short             2
	//   4       unsigned long              4
	//   5       unsigned rational          8
	//   6       signed byte                1
	//   7       undefined                  1
	//   8       signed short               2
	//   9       signed long	            4
	//   10      signed rational            8
	//   11      single float               4
	//   12      double float               8
	uint16_t bytesPerComponent[12] = {1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
	if(type < 0 || type > 12)
	{
		printf("type %d is error!(in[1:12])\n", type);
		return 0;
	}
	else
	{
		return bytesPerComponent[type-1];
	}
}

/**
 * @brief  jpeg_app1_info
 * @note   函数中的[relative addr]表示假设图片的起始地址为0时的相对地址（方便在Hex阅读器中查看）
 * @param [in]
 * @param [out]
 * @retval 
 */
//解析app1的信息
int jpeg_app1_info(uint8_t *addr, uint32_t readIdx)
{
	uint8_t *pJpegSection = NULL;
	uint8_t *pTiffHead = NULL;
	uint32_t tiffHeadRelative = 0;
	
	uint16_t idx = 0;
	uint16_t i = 0;
	uint16_t ifd0EntryNum = 0;
    uint16_t ifd0Offset = 0;
	uint16_t ifd1Offset = 0;
	uint16_t thumbnailOffset = 0;
	uint16_t ifd1EntryNum = 0;

    uint16_t ifdxEntryComponentType = 0;
    uint16_t ifdxEntryComponentSize = 0;

	uint8_t tagInfoStr[64] = {0};
	uint8_t tagValueStr[128] = {0};
    pJpegSection = addr;

	printf("==================== APP1 ====================\n");
	printf("段标识:%02x %02x\n", *(pJpegSection + idx), *(pJpegSection + idx + 1));
	idx += 2;
	
	printf("段长度:%02x %02x (%d)\n", *(pJpegSection + idx), *(pJpegSection + idx + 1), MERGE_2BYTE(pJpegSection, idx));
	idx += 2;
	
	printf("标识符:%02x %02x %02x %02x %02x %02x\n", 
			*(pJpegSection + idx), *(pJpegSection + idx + 1),
			*(pJpegSection + idx + 2), *(pJpegSection + idx + 3),
			*(pJpegSection + idx + 4), *(pJpegSection + idx + 5));
	idx += 6;
	// TIFF头
    pTiffHead = pJpegSection + idx; // 记录TIFF头的位置，后续有很多都是以此地址为基准进行偏移
    tiffHeadRelative = (uint32_t)RELATIVE_ADDR(pTiffHead, pJpegAddr);
    printf("[relative addr]TIFF Head = 0x%08x,TIFF Head Addr = %p, JPEG start Addr = %p,  idx = 0x%08x(+0x%08x=0x%08x)\n", 
            tiffHeadRelative, pTiffHead, pJpegAddr, idx, readIdx, idx + readIdx);
    
	printf("TIFF头:%02x %02x %02x %02x %02x %02x %02x %02x\n", 
			*(pJpegSection + idx), *(pJpegSection + idx + 1),
			*(pJpegSection + idx + 2), *(pJpegSection + idx + 3),
			*(pJpegSection + idx + 4), *(pJpegSection + idx + 5),
			*(pJpegSection + idx + 6), *(pJpegSection + idx + 7));
	idx +=0;
	printf("       %02x %02x (%c%c)       字节序，II为intel字节序，即小端字节序，MM为Motorola字节序，即大端字节序\n", 
			*(pJpegSection + idx), *(pJpegSection + idx + 1), *(pJpegSection + idx), *(pJpegSection + idx + 1));
	idx += 2;
	printf("       %02x %02x            Tag Mark，Intel字节序，则对应的存储值为 0x2a00，Motorola 的字节序，则存储值为 0x002a\n", 
			*(pJpegSection + idx), *(pJpegSection + idx + 1));
	idx += 2;
	printf("       %02x %02x %02x %02x (%d)  到IFD0的偏移，从TIFF头起始位置开始计算\n", 
			*(pJpegSection + idx), *(pJpegSection + idx + 1), 
			*(pJpegSection + idx + 2), *(pJpegSection + idx + 3),
			MERGE_4BYTE(pJpegSection, idx));
    ifd0Offset = MERGE_4BYTE(pJpegSection, idx);
	idx += 4;
    // IFD0
	printf("[relative addr]IFD0=0x%08x\n", tiffHeadRelative + ifd0Offset);
	printf("IFD0  :%02x %02x (%d 个Entry, IFD0 relative addr=0x%08x)\n", 
            *(pJpegSection + idx), *(pJpegSection + idx + 1), MERGE_2BYTE(pJpegSection, idx),
            tiffHeadRelative + ifd0Offset);
	ifd0EntryNum = MERGE_2BYTE(pJpegSection, idx);
	idx += 2;
	
    // 获取每个TAG的类型并用字符串进行描述
	printf("      | TAG  | TYPE  |  LENGTH   |   OFFSET  |\n");
	for(i = 0; i < ifd0EntryNum; i++)
	{
        switch(MERGE_2BYTE(pJpegSection, idx))
        {
            case 0x010f: // Make, Shows manufacturer of digicam
                snprintf((char *)tagInfoStr, sizeof(tagInfoStr), "Make");
            case 0x0110: // Model, Shows model number of digicam
                snprintf((char *)tagInfoStr, sizeof(tagInfoStr), "Model");
                break;
            case 0x011a: // XResolution
                snprintf((char *)tagInfoStr, sizeof(tagInfoStr), "XResolution");
                break;
            case 0x011b: // YResolution
                snprintf((char *)tagInfoStr, sizeof(tagInfoStr), "YResolution");
                break;
            case 0x0128: // ResolutionUnit, Unit of XResolution(0x011a)/YResolution(0x011b). '1' means inch, '2' means centimeter.
                snprintf((char *)tagInfoStr, sizeof(tagInfoStr), "ResolutionUnit");
                break;
            case 0x0132: // DateTime
                snprintf((char *)tagInfoStr, sizeof(tagInfoStr), "DateTime");
                break;
            case 0x0213: // YCbCrPositioning
                snprintf((char *)tagInfoStr, sizeof(tagInfoStr), "YCbCrPositioning");
                break;
            case 0x8769: // ExifOffset, Offset to Exif Sub IFD
                snprintf((char *)tagInfoStr, sizeof(tagInfoStr), "ExifOffset");
                break;
            case 0x8825: // GPSInfo,
                snprintf((char *)tagInfoStr, sizeof(tagInfoStr), "GPSInfo");
                break;

            default:
                memset(tagInfoStr, 0, sizeof(tagInfoStr));
                break;
        }
        //获取组件的总大小
		ifdxEntryComponentType = MERGE_2BYTE(pJpegSection, idx + 2);
		ifdxEntryComponentSize = jpeg_getComponentSize(ifdxEntryComponentType) * MERGE_4BYTE(pJpegSection, idx + 4);
		if(ifdxEntryComponentSize > 4)
		{
			snprintf((char *)tagValueStr, sizeof(tagValueStr), "[0x%08x]",  tiffHeadRelative + MERGE_4BYTE(pJpegSection, idx + 8));
		}
		else if(ifdxEntryComponentSize <= 4 && ifdxEntryComponentSize > 2)
		{
			snprintf((char *)tagValueStr, sizeof(tagValueStr), "[%d]",  MERGE_4BYTE(pJpegSection, idx + 8));
		}
		else if(ifdxEntryComponentSize <= 2 && ifdxEntryComponentSize > 0)
		{
			snprintf((char *)tagValueStr, sizeof(tagValueStr), "[%d]",  MERGE_2BYTE(pJpegSection, idx + 8));
		}
		else
		{
			memset(tagValueStr, 0, sizeof(tagValueStr));
		}
		printf("      |%02x %02x | %02x %02x |%02x %02x %02x %02x|%02x %02x %02x %02x| %32s %s\n", 
				*(pJpegSection + idx), *(pJpegSection + idx + 1),
				*(pJpegSection + idx + 2), *(pJpegSection + idx + 3),
				*(pJpegSection + idx + 4), *(pJpegSection + idx + 5),*(pJpegSection + idx + 6), *(pJpegSection + idx + 7),
				*(pJpegSection + idx + 8), *(pJpegSection + idx + 9),*(pJpegSection + idx + 10), *(pJpegSection + idx + 11),
                tagInfoStr, tagValueStr);
		idx += 12;
	}
	printf("      %02x %02x %02x %02x (到下一个IFD的偏移为 %d,为0时表示这是最后一个IFD)\n", 
			*(pJpegSection + idx), *(pJpegSection + idx + 1), 
			*(pJpegSection + idx + 2), *(pJpegSection + idx + 3),
			MERGE_4BYTE(pJpegSection, idx));
	ifd1Offset = MERGE_4BYTE(pJpegSection, idx);
	idx += 4;
	// IFD1
	idx = 10 + ifd1Offset;
    printf("[relative addr]IFD1=0x%08x\n", tiffHeadRelative + ifd1Offset);
	printf("IFD1  :%02x %02x (%d 个Entry)\n", *(pJpegSection + idx), *(pJpegSection + idx + 1), MERGE_2BYTE(pJpegSection, idx));
	ifd1EntryNum = MERGE_2BYTE(pJpegSection, idx);
	idx += 2;

	printf("      | TAG  | TYPE  |  LENGTH   |   OFFSET  |\n");
	for(i = 0; i < ifd1EntryNum; i++)
	{
        switch(MERGE_2BYTE(pJpegSection, idx))
        {
            case 0x0103:
                snprintf((char *)tagInfoStr, sizeof(tagInfoStr), "Compression");
                break;
            case 0x011a: //XResolution
                snprintf((char *)tagInfoStr, sizeof(tagInfoStr), "XResolution");
                break;
            case 0x011b: //YResolution
                snprintf((char *)tagInfoStr, sizeof(tagInfoStr), "YResolution");
                break;
            case 0x0128: //ResolutionUnit
                snprintf((char *)tagInfoStr, sizeof(tagInfoStr), "ResolutionUnit");
                break;
            case 0x0201:
                snprintf((char *)tagInfoStr, sizeof(tagInfoStr), "JpegIFOffset");
                thumbnailOffset = MERGE_4BYTE(pJpegSection, idx + 8);
                break;
            case 0x0202:
                snprintf((char *)tagInfoStr, sizeof(tagInfoStr), "JpegIFByteCount");
                break;
            default:
                memset(tagInfoStr, 0, sizeof(tagInfoStr));
                break;
        }
        //获取组件的总大小
		ifdxEntryComponentType = MERGE_2BYTE(pJpegSection, idx + 2);
		ifdxEntryComponentSize = jpeg_getComponentSize(ifdxEntryComponentType) * MERGE_4BYTE(pJpegSection, idx + 4);
		if(ifdxEntryComponentSize > 4)
		{
			snprintf((char *)tagValueStr, sizeof(tagValueStr), "[0x%08x]",  tiffHeadRelative + MERGE_4BYTE(pJpegSection, idx + 8));
		}
		else if(ifdxEntryComponentSize <= 4 && ifdxEntryComponentSize > 2)
		{
			snprintf((char *)tagValueStr, sizeof(tagValueStr), "[%d]",  MERGE_4BYTE(pJpegSection, idx + 8));
		}
		else if(ifdxEntryComponentSize <= 2 && ifdxEntryComponentSize > 0)
		{
			snprintf((char *)tagValueStr, sizeof(tagValueStr), "[%d]",  MERGE_2BYTE(pJpegSection, idx + 8));
		}
		else
		{
			memset(tagValueStr, 0, sizeof(tagValueStr));
		}
        printf("      |%02x %02x | %02x %02x |%02x %02x %02x %02x|%02x %02x %02x %02x| %32s %s\n", 
                *(pJpegSection + idx), *(pJpegSection + idx + 1),
                *(pJpegSection + idx + 2), *(pJpegSection + idx + 3),
                *(pJpegSection + idx + 4), *(pJpegSection + idx + 5),*(pJpegSection + idx + 6), *(pJpegSection + idx + 7),
                *(pJpegSection + idx + 8), *(pJpegSection + idx + 9),*(pJpegSection + idx + 10), *(pJpegSection + idx + 11),
                tagInfoStr, tagValueStr);

		idx += 12;
	}
	printf("      %02x %02x %02x %02x (到下一个IFD的偏移为 %d,为0时表示这是最后一个IFD)\n", 
			*(pJpegSection + idx), *(pJpegSection + idx + 1), 
			*(pJpegSection + idx + 2), *(pJpegSection + idx + 3),
			MERGE_4BYTE(pJpegSection, idx));
	idx += 4;
	//缩略图信息
	idx = 10 + thumbnailOffset;
    printf("[relative addr]thumbnail=0x%08x\n", tiffHeadRelative + thumbnailOffset);
	printf("缩略图:%02x %02x %02x %02x\n", 
			*(pJpegSection + idx), *(pJpegSection + idx + 1), 
			*(pJpegSection + idx + 2), *(pJpegSection + idx + 3));
	printf("APP1只解析了重要内容\n");
	printf("==============================================\n");
	return 0;
}

//解析sofo0的信息
int jpeg_sof0_info(uint8_t *addr)
{
	uint8_t *pJpegSection = NULL;
	pJpegSection = addr;
	uint16_t idx = 0;
	
	printf("==================== SOF0 ====================\n");
	printf("段标识:%02x %02x\n", *(pJpegSection + idx), *(pJpegSection + idx + 1));
	idx += 2;
	
	printf("段长度:%02x %02x (%d)\n", *(pJpegSection + idx), *(pJpegSection + idx + 1), MERGE_2BYTE(pJpegSection, idx));
	idx += 2;
	
	printf("精度  :%02x\n", *(pJpegSection + idx));
	idx += 1;
	
	printf("高度  :%02x %02x (%d)\n", *(pJpegSection + idx), *(pJpegSection + idx + 1), MERGE_2BYTE(pJpegSection, idx));
	idx += 2;
	
	printf("宽度  :%02x %02x (%d)\n", *(pJpegSection + idx), *(pJpegSection + idx + 1), MERGE_2BYTE(pJpegSection, idx));
	idx += 2;
	
	printf("组件数:%02x\n", *(pJpegSection + idx));
	idx += 1;
	
	printf("组件ID:%02x 采样系数:%02x 量化表号:%02x\n", *(pJpegSection + idx), *(pJpegSection + idx + 1), *(pJpegSection + idx + 2));
	idx += 3;
	
	printf("组件ID:%02x 采样系数:%02x 量化表号:%02x\n", *(pJpegSection + idx), *(pJpegSection + idx + 1), *(pJpegSection + idx + 2));
	idx += 3;
	
	printf("组件ID:%02x 采样系数:%02x 量化表号:%02x\n", *(pJpegSection + idx), *(pJpegSection + idx + 1), *(pJpegSection + idx + 2));
	idx += 3;
	
	printf("SOF0解析完毕，共解析%d字节(包含了段标识，段长度为%d字节)\n", idx, idx - 2);
	printf("==============================================\n");
	return 0;
}

int main(int argc, const char *argv[])
{
	FILE *fp = NULL;
	
	uint32_t jpegLen = 0;
	uint32_t readLen = 0;
	uint16_t mark = 0;
	uint16_t blockLen = 0;
	uint32_t readIdx = 0;
	
	char jpegFileName[128] = {0};
	if (argc != 2) 
	{
        fprintf(stderr, "用法: %s <filename>\n", argv[0]);
        return -1;
    }
	snprintf(jpegFileName, sizeof(jpegFileName), "%s", argv[1]);
	fp = fopen((char *)jpegFileName, "rb");
	if (fp == NULL)
	{
		perror("fopen ");
		printf("%s open failed!!!\n", jpegFileName);
		return -1;
	}
	fseek(fp, 0, SEEK_END);
	jpegLen = ftell(fp);
	if(jpegLen <= 0)
	{
		printf("jpegFileName %s 's size error!\n", jpegFileName);
		return -1;
	}
	fseek(fp, 0, SEEK_SET);
	//申请内存
	pJpegAddr = (uint8_t *)malloc(jpegLen);
	if(pJpegAddr == NULL)
	{
		printf("malloc fail!\n");
		return -1;
	}
	printf("jpegFileName=%s, open success, size is %d, pJpegAddr=%p!sizeof(uint8_t)=%ld sizeof(uint16_t)=%ld sizeof(uint32_t)=%ld\n", 
			jpegFileName, jpegLen, pJpegAddr, sizeof(uint8_t), sizeof(uint16_t), sizeof(uint32_t));
	//读取数据到内存中
	readLen = fread(pJpegAddr, 1, jpegLen, fp);
	if(readLen < jpegLen)
	{
		printf("read fail from %s!\n", jpegFileName);
		return -1;
	}
	fclose(fp);
	// 读取JPEG数据
	while((readIdx + 2) < jpegLen)
	{
		mark = pJpegAddr[readIdx] << 8 | pJpegAddr[readIdx + 1];
		if (readIdx == 0 && mark != JPEG_SOI) //开头两个字节检查是否是JPEG文件
		{
			fclose(fp);
			printf("%s is not JPEG!!!\n", jpegFileName);
			return -1; // 不是JPEG文件
		}
		
		if(mark == JPEG_APP1) //EXIF格式的JPEG
		{
			blockLen = pJpegAddr[readIdx + 2] << 8 | pJpegAddr[readIdx + 3];
			jpeg_app1_info(&pJpegAddr[readIdx], readIdx);
			readIdx += blockLen + 2;
		}
		else if(mark == JPEG_SOF0)
		{
			blockLen = pJpegAddr[readIdx + 2] << 8 | pJpegAddr[readIdx + 3];
			jpeg_sof0_info(&pJpegAddr[readIdx]);
			readIdx += blockLen + 2;
		}
		else
		{
			readIdx++;
		}
	}
	printf("read end!readIdx=%d\n", readIdx);
	
	free(pJpegAddr);
	
	return 0;
}